#ifndef VMM_H
#define VMM_H

#include <stdint.h>
#include "pmm.h"

// -------------------------- 核心地址与页大小定义 --------------------------
// 物理内存起始地址（与QEMU启动地址一致，RISC-V通常为0x80000000）
#define PHYSBASE 0x80000000UL
// 物理内存结束地址（128MB内存：0x80000000 + 128*1024*1024）
#define PHYSTOP  0x88000000UL
// 内核虚拟地址起始（Sv39高地址区，与物理地址偏移）
#define KERNBASE 0xFFFF800000000000UL
// 页大小（4KB，RISC-V标准页大小）
#define PGSIZE   4096UL
// 页对齐宏（向上/向下对齐到PGSIZE）
#define PGROUNDUP(sz)  (((sz) + PGSIZE - 1) & ~(PGSIZE - 1))  // 向上取整到页边界
#define PGROUNDDOWN(a) ((a) & ~(PGSIZE - 1))                  // 向下取整到页边界

// UART设备物理地址（QEMU的UART0通常为0x10000000）
#define UART0    0x10000000UL

// -------------------------- 页表项（PTE）定义 --------------------------
typedef uint64_t pte_t;
typedef uint64_t* pagetable_t;  // 页表是指向512个PTE的数组（Sv39三级页表）

// 页表项权限位
#define PTE_V (1ULL << 0)  // 有效位（必须置1，否则PTE无效）
#define PTE_R (1ULL << 1)  // 读权限（允许读操作）
#define PTE_W (1ULL << 2)  // 写权限（允许写操作）
#define PTE_X (1ULL << 3)  // 执行权限（允许执行操作）
#define PTE_U (1ULL << 4)  // 用户模式位（允许用户态访问）
#define PTE_G (1ULL << 5)  // 全局页位（TLB刷新时不失效）
#define PTE_A (1ULL << 6)  // 访问位（CPU访问后自动置1）
#define PTE_D (1ULL << 7)  // 脏位（CPU写入后自动置1）

// 物理地址与页表项转换
#define PA2PTE(pa) ((((uint64_t)(pa)) >> 2) << 2)  // 物理地址转PTE（PA需4字节对齐）
#define PTE2PA(pte) (((pte) >> 2) << 2)            // 从PTE提取物理地址

// -------------------------- Sv39页表结构定义 --------------------------
#define PG_LEVELS 3  // 三级页表（L0=页内偏移，L1/L2/L3=页表级）

// 虚拟页号（VPN）解析（Sv39虚拟地址：[9位L2 VPN][9位L1 VPN][9位L0 VPN][12位页内偏移]）
#define VPN_SHIFT(level) (12 + 9 * (level))  // 第level级VPN的起始偏移（level=0/1/2）
#define VPN_MASK(va, level) (((va) >> VPN_SHIFT(level)) & 0x1FF)  // 提取第level级VPN（9位）

// -------------------------- SATP寄存器操作 --------------------------
#define SATP_MODE_SV39  (8ULL << 60)  // Sv39模式标识
#define MAKE_SATP(pt)   (SATP_MODE_SV39 | ((uint64_t)(pt) >> 12))  // 构造SATP值

// -------------------------- 链接脚本导出符号 --------------------------
extern char _text[];    // 代码段起始物理地址
extern char _etext[];   // 代码段结束物理地址
extern char _edata[];   // 数据段结束物理地址
extern char _end[];     // 内核镜像结束物理地址

// -------------------------- 全局内核页表声明 --------------------------
extern pagetable_t kernel_pagetable;  // 内核根页表（在vmm.c中定义）

// -------------------------- 页表操作接口 --------------------------
pagetable_t create_pagetable(void);   // 创建空页表
int map_page(pagetable_t pt, uint64_t va, uint64_t pa, int perm);  // 映射单个虚拟页
void map_region(pagetable_t pt, uint64_t va, uint64_t pa, uint64_t size, int perm);  // 映射连续区域
void destroy_pagetable(pagetable_t pt);  // 递归销毁页表
void dump_pagetable(pagetable_t pt, int level);  // 打印页表结构

// 虚拟内存初始化接口（新增：解决隐式声明警告）
void kvm_init(void);  // 初始化并激活虚拟内存

// 内部辅助函数（对外不可见，仅声明不实现）
pte_t* walk_create(pagetable_t pt, uint64_t va);  // 查找或创建PTE
pte_t* walk_lookup(pagetable_t pt, uint64_t va);  // 查找PTE（不创建）

#endif
